/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.veditor.view;

import java.util.LinkedList;
import java.util.List;
import javax.swing.JTree;
import javax.swing.text.Element;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import offset.nodes.Constants;
import offset.nodes.client.model.HierarchyTree;
import offset.nodes.client.model.NodeTypes;
import offset.nodes.client.virtual.model.SchemaNode;
import offset.nodes.client.virtual.model.SchemaProperty;
import offset.nodes.client.model.SimpleNamespaceRegistry;
import offset.nodes.client.virtual.model.jcr.QName;
import offset.nodes.client.virtual.model.jcr.nodetype.NodeTypeDefinition;
import offset.nodes.client.view.MappedSchemaNodeRenderer;
import offset.nodes.client.view.SchemaNodeRenderer;
import offset.nodes.client.view.TreeNodeChooserDialog;
import offset.nodes.client.veditor.model.DataModel;

/**
 *
 * @author  Walter Luetgenau
 */
public class SelectSchemaDialog {

    JTree structure;
    NodeTypes types;
    SimpleNamespaceRegistry namespaces = SimpleNamespaceRegistry.getInstance();
    String[] mappedPathes;
    
    /** A return status code - returned if Cancel button has been pressed */
    public static final int RET_CANCEL = 0;
    /** A return status code - returned if OK button has been pressed */
    public static final int RET_OK = 1;
    private int returnStatus = RET_CANCEL;
    public static final int SCHEMA_NEW = 1;
    public static final int SCHEMA_EXTENDED = 2;
    private int schemaType = SCHEMA_NEW;
    String schemaName = null;
    Object schemaObject = null;

    /** Creates new form SelectSchemaDialog */
    public SelectSchemaDialog(JTree structure, NodeTypes types, int schemaType) {
        this.structure = structure;
        this.types = types;
        this.schemaType = schemaType;
        mappedPathes = getMappedPathes(structure);
    }
    
    public void show() {
        if (schemaType == SCHEMA_NEW)
            showNewType();
        else
            showExtendedType();
    }
    
    protected DefaultMutableTreeNode setupNewTree(NodeTypeDefinition[] definitions) {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode();
        for (int i = 0; i < definitions.length; i++) {
            root.add(HierarchyTree.setupHierarchicalTree(definitions[i].getName(), definitions[i], null, types, true, true));
        }

        return root;
    }

    protected void findMappedPathes(DefaultMutableTreeNode node, String rootPath, List mappedPathes) {
        if (node.getUserObject() != null && node.getUserObject() instanceof Element) {
            Element e = (Element) node.getUserObject();
            String query = (String) getDefinedAttribute(e, DataModel.ATTRIBUTE_NAME_DATA);
            if (query != null) {
                if (query.startsWith(rootPath)) {
                    String name = query.substring(rootPath.length());
                    name = name.replaceAll("\\[.*\\]", "");
                    name = name.replaceAll("@", "");
                    name = name.replaceAll("/", "");
                    rootPath = rootPath + "/" + name;
                    mappedPathes.add(rootPath);
                } else {
                    return;
                } // this is a new root query. Don't traverse the subtree.
            }
        }

        for (int i = 0; i < node.getChildCount(); i++) {
            findMappedPathes((DefaultMutableTreeNode) node.getChildAt(i), rootPath, mappedPathes);
        }
    }

    protected Object getDefinedAttribute(Element e, String name) {
        if (e.getAttributes().isDefined(name)) {
            return e.getAttributes().getAttribute(name);
        }
        return null;
    }
    
    protected String[] getMappedPathes(JTree tree) {
        LinkedList mappedPathes = new LinkedList();

        DefaultMutableTreeNode selected = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
        DefaultMutableTreeNode node = selected;
        DefaultMutableTreeNode root = null;
        while (node != null) {
            if (node.getUserObject() instanceof Element) {
                Element e = (Element) node.getUserObject();
                String query = (String) getDefinedAttribute(e, DataModel.ATTRIBUTE_NAME_DATA);
                if (query != null && (query.indexOf("/") < 0 || DataModel.isSecondaryQueryRoot(query)) && node != selected) { // found root query != selected
                    if ( query.indexOf("[") >= 0)
                        query = query.replaceAll("\\[.*\\]", "");
                    root = node;
                    mappedPathes.add(query);
                    break;
                }
            }

            node = (DefaultMutableTreeNode) node.getParent();
        }

        if (root == null) {
            return new String[0];
        }

        for (int i = 0; i < root.getChildCount(); i++) {
            findMappedPathes((DefaultMutableTreeNode) root.getChildAt(i), (String) mappedPathes.get(0), mappedPathes);
        }
        return (String[]) mappedPathes.toArray(new String[mappedPathes.size()]);
    }
    
    protected String getPath(DefaultMutableTreeNode node) {
        TreeNode[] path = node.getPath();
        
        StringBuffer buf = new StringBuffer();
        for( int i=0; i<path.length; i++) {
            buf.append(path[i].toString());
            if ( i+1 < path.length)
                buf.append("/");
        }
        
        return buf.toString();      
    }
    
    protected String  getSecondaryQuery(String path) {
        if ( !path.startsWith(DataModel.SECONDARY_QUERY))
            return null;
        
        return path.substring(0,path.indexOf("/", DataModel.SECONDARY_QUERY.length()));
    }
    
    protected String getPath(String secondaryQuery) {
        if ( !secondaryQuery.startsWith(DataModel.SECONDARY_QUERY) )
            return secondaryQuery;
        
        return secondaryQuery.substring(secondaryQuery.indexOf("/", DataModel.SECONDARY_QUERY.length())+1);
     }
    
    protected String[] getPathes(String[] secondaryQueries) {
        String[] pathes = new String[secondaryQueries.length];
        
        for( int i=0; i<secondaryQueries.length; i++)
            pathes[i] = getPath(secondaryQueries[i]);
        
        return pathes;
    }

    private void showExtendedType() {
        String secondaryQuery = getSecondaryQuery(mappedPathes[0]);
        if ( secondaryQuery != null ) {
            mappedPathes = getPathes(mappedPathes);
        }

        String nodeTypeName = namespaces.toQNameString(lastComponent(mappedPathes[0])); // the first path is the root element. The name consists of the nodeType name only.
        NodeTypeDefinition nodeType = types.get(QName.valueOf(nodeTypeName));
        if (nodeType == null) {
            return;
        }

        TreeNodeChooserDialog chooser = new TreeNodeChooserDialog(null, true, HierarchyTree.setupHierarchicalTree(QName.ANY_NAME, nodeType, null, types, true, true));
        chooser.setCellRenderer(new MappedSchemaNodeRenderer(mappedPathes));
        chooser.setVisible(true);
        if (chooser.getReturnStatus() == TreeNodeChooserDialog.RET_OK) {
            DefaultMutableTreeNode selected = (DefaultMutableTreeNode) chooser.getSelectedNode();
            String query = "";
            if (selected.getUserObject() instanceof SchemaProperty) {
                    query = getPath((DefaultMutableTreeNode) selected.getParent()) + "/@" + selected.toString();
            } else if (selected.getUserObject() instanceof SchemaNode) {
                if (selected.getParent() != null) {
                    query = getPath((DefaultMutableTreeNode) selected.getParent()) + "/" + selected.toString();
                } else {
                    query = selected.toString();
                }
            }
            
            if ( secondaryQuery != null) {
                query = secondaryQuery + "/" + query;
            }
            
            setResult(RET_OK, selected.getUserObject(), query);
        }
        
        
}

    private void showNewType() {
        TreeNodeChooserDialog chooser = new TreeNodeChooserDialog(null, true, 
                setupNewTree(types.getArray(new QName[] {new QName(Constants.URI_NODES_DATA, ""), new QName(Constants.URI_NODES, "")})));
        chooser.setCellRenderer(new SchemaNodeRenderer());
        chooser.setVisible(true);
        if (chooser.getReturnStatus() == TreeNodeChooserDialog.RET_OK) {
            DefaultMutableTreeNode selected = (DefaultMutableTreeNode) chooser.getSelectedNode();
            String query = "";
            if (selected.getUserObject() instanceof SchemaNode) {
                query = selected.toString();
            }
            
            setResult(RET_OK, selected.getUserObject(), query);
         }
}
    
    protected void setResult(int returnStatus, Object schemaObject, String schemaName) {
        setReturnStatus(returnStatus);
        setSchemaObject(schemaObject);
        setSchemaName(schemaName);
    }

    public Object getSchemaObject() {
        return schemaObject;
    }

    public void setSchemaObject(Object schemaObject) {
        this.schemaObject = schemaObject;
    }

    public void setSchemaName(String schemaName) {
        this.schemaName = schemaName;
    }

    public String getSchemaName() {
        return schemaName;
    }

    public int getSchemaType() {
        return schemaType;
    }
    
    protected String lastComponent(String path) {
        int index = path.lastIndexOf("/");
        if ( index < 0)
            return path;
        return path.substring(index+1);
    }

    public int getReturnStatus() {
        return returnStatus;
    }

    public void setReturnStatus(int returnStatus) {
        this.returnStatus = returnStatus;
    }
}
